/**
 * @author Shen Yi - bm2736892@gmail.com
 */

/**
 * 
 * @param {String} name
 * @param {Object} client
 * @param {Object} pack
 * @param {Object} o3d
 * @param {Transform} transformRoot
 * @param {object} renderNode
 * @param {!string} textureName need when the view is render to a texture
 * @param {!Array} clearColor
 */
var Camera = function(name, pack, client, o3d, transformRoot, renderNode, textureName, clearColor){
	clearColor = isset(clearColor) ? clearColor : [0, 0, 0.4 , 1];
	//=========================public property======================
	this.name = name;
	
	//=========================private property=====================
 	this._client = client;
	this._pack = pack;
	this._o3d = o3d;
	this._math = o3djs.math;
	
	this._transformRoot = transformRoot;
	this._renderNode = renderNode;
	
	this._pass = [];
	//transform tree info for picking
	this._transformTreeInfo = o3djs.picking.createTransformInfo(this._transformRoot, null);
	
	this._matrix4 = o3djs.math.matrix4;
	
	this._cameraInfo = {
		eye : [],
		target : [],
		up : []
	}
	this.backupCameraInfo = [];
	
	if(isset(textureName)){
		var renderSurfaceSet = TextureManager.getRenderSurfaceSet(textureName);
		if(isset(renderSurfaceSet)){
			this._renderSurfaceSet = renderSurfaceSet;
			renderSurfaceSet.parent = renderNode;
			this._viewInfo = o3djs.rendergraph.createBasicView(pack, transformRoot, renderSurfaceSet, clearColor);
		}
		else{
			alert("Camera.js : can not render to texture : " + textureName);
		}
	}
	else{
	 	this._viewInfo = o3djs.rendergraph.createBasicView(pack, transformRoot, renderNode, clearColor);
	}
 }
 
 Camera.prototype = {
 	getViewInfo : function(){
		return this._viewInfo;
	},
	/**
	 * @return cameraInfo {eye, target, up}
	 */
	getCameraInfo : function(){
		return this._cameraInfo;
	},
	
	getCameraMatrix : function(){
		return this._viewInfo.drawContext.view;
	},
	
	getProjectionMatrix : function(){
		return this._viewInfo.drawContext.projection;
	},
	
	getRenderNode : function(){
		return this._renderNode;
	},
	
	getTransformRoot : function(){
		return this._transformRoot;
	},
	
	getPass : function(name){
		if(isset(name)){
			return this._pass[name];
		}
		return this._pass;
	},
	
	disable : function()
	{
		this._renderNode.parent = null;
	},
	
	enable : function()
	{
		this._renderNode.parent = this._client.renderGraphRoot;
	},
	
	pushCameraInfo : function()
	{
		this._backupCameraInfo.push(this._cameraInfo);
	},
	
	popCameraInfo : function()
	{
		return this._backupCameraInfo.pop();	
	},
	/**
	 * 
	 * @param {!Int} aspect
	 * @param {!Float} ratio
	 * @param {!Float} near
	 * @param {!Float} far
	 */
	setProjection : function(aspect, ratio,  near, far){
		aspect = isset(aspect) ? aspect : 30;
		ratio = isset(ratio) ? ratio : this._client.width / this._client.height;
	 	near = isset(near) ? near : 1;
		far = isset(far) ? far : 5000;
		this._viewInfo.drawContext.projection = this._matrix4.perspective(this._math.degToRad(aspect), ratio, near, far);
		
		return this;
	},
	/**
	 * 
	 * @param {matrix4x4} matrix
	 */
	setProjectionMatrix : function(matrix){
		this._viewInfo.drawContext.projection = matrix;
	},
	/**
	 * 
	 * @param {!Float} left
	 * @param {!Float} right
	 * @param {!Float} bottom
	 * @param {!Float} top
	 * @param {!Float} near
	 * @param {!Float} far
	 */
	setOrthographic : function(left, right, bottom, top, near, far){
		left = isset(left) ? left : -1;
		right = isset(right) ? right : 1;
		bottom = isset(bottom) ? bottom : -1;
		top = isset(top) ? top : 1;
		near = isset(near) ? near : -1;
		far = isset(far) ? far : 1;
		this._viewInfo.drawContext.projection = this._matrix4.orthographic(left, right, bottom, top, near, far);
		this._viewInfo.drawContext.view = this._matrix4.identity();
		
		return this;
	},
	/**
	 * 
	 * @param {Float3} eye
	 * @param {Float3} target
	 * @param {Float3} up
	 */
	setCamera : function(eye, target, up){
		this._cameraInfo.eye = eye;
		this._cameraInfo.target = target;
		this._cameraInfo.up = up;
		
		this.updateCamera();
		return this;
	},
	/**
	 * we mainly create a new pass for blend with the other scene
	 * @param {String} name
	 * @param {Object} option
	 */
	createPass : function(name, option){
		//add the default value
		option.AlphaBlendEnable = isset(option.AlphaBlendEnable) ? option.AlphaBlendEnable : false;
		option.SourceBlendFunction = isset(option.SourceBlendFunction) ? option.SourceBlendFunction : "BLENDFUNC_ONE";
		option.DestinationBlendFunction = isset(option.DestinationBlendFunction) ? option.DestinationBlendFunction : "BLENDFUNC_ONE";
		option.BlendEquation = isset(option.BlendEquation) ? option.BlendEquation : "BLEND_ADD"
		//create a drawPass
		var pass = this._viewInfo.createDrawPass(this._o3d.DrawList.BY_PERFORMANCE);
		var state = pass.state;
		for(var key in option){
			if(typeof(option[key]) == "string"){
				eval("state.getStateParam('"+key+"').value = o3djs.base.o3d.State."+option[key]);
			}
			else{
				eval("state.getStateParam('"+key+"').value = option[key]");
			}
		}
		this._pass[name] = pass;
		pass.name = name;
		return pass;
	} ,
	
	updateCamera : function(){
		this._viewInfo.drawContext.view = this._matrix4.lookAt(this._cameraInfo.eye, this._cameraInfo.target, this._cameraInfo.up);
	},
	
	/**
	 * 
	 * @param {Integer} clientX
	 * @param {Integer} clientY
	 * @return {ShapeInfo}
	 */
	pickShape : function(clientX, clientY){
		var worldRay = o3djs.picking.clientPositionToWorldRay(clientX, clientY, this._viewInfo.drawContext, this._client.width, this._client.height);
		this._transformTreeInfo.update();
		var pickInfo = this._transformTreeInfo.pick(worldRay);
		return pickInfo.shapeInfo;
	},
	
	/////////////////////////camera animation
	/**
	 * 
	 * @param {Object} option  {eyeTo, targetTo, step}
	 */
	moveTo : function(option){
		//step = 1 means move to the eyeTo
		if(isset(option.step) && option.step && option.step != 1){
			if(isset(option.eyeTo)){
				var ray = this._math.subVector(option.eyeTo, this._cameraInfo.eye);
				var rayStep = this._math.mulVectorScalar(ray, option.step);
				this._cameraInfo.eye = this._math.addVector(this._cameraInfo.eye, rayStep);
			}
			if(isset(option.targetTo)){
				var ray = this._math.subVector(option.targetTo, this._cameraInfo.target);
				var rayStep = this._math.mulVectorScalar(ray, option.step);
				this._cameraInfo.target = this._math.addVector(this._cameraInfo.target, rayStep);
			}
		}
		else{
			this._cameraInfo.eye = isset(option.eyeTo) ? option.eyeTo : this._cameraInfo.eye;
			this._cameraInfo.target = isset(option.target) ? option.targetTo : this._cameraInfo.target;
		}
		
		this.updateCamera();
	},
	/**
	 * 
	 * @param {Object} option {eyeAlong, targetAlong, step}
	 */
	moveAlong : function(option){
		if(isset(option.eyeAlong)){
			var rayStep = this._math.mulVectorScalar(option.eyeAlong, option.step);
			this._cameraInfo.eye = this._math.addVector(this._cameraInfo.eye, rayStep);
		}
		if(isset(option.targetAlong)){
			var rayStep = this._math.mulVectorScalar(option.targetAlong, option.step);
			this._cameraInfo.target = this._math.addVector(this._cameraInfo.target, rayStep);
		}
		$("#debug").html(dumpVector(this._cameraInfo.target));
		this.updateCamera();
	},
	
	/**
	 * 
	 * @param {Object} option {aroundAxis, aroundPos, step}
	 */
	moveEyeAround : function(option){
		var q = o3djs.quaternions;
		var m = this._matrix4;
		
		var quaternion = q.axisRotation(option.aroundAxis, option.step);
		var matrix4 = q.quaternionToRotation(quaternion);
		//sub the aroundPos and add the aroundPos is to make sure the target is rotate around the aroundPos
		this._cameraInfo.eye = m.transformPoint(matrix4, this._math.subVector(this._cameraInfo.eye, option.aroundPos));
		this._cameraInfo.eye = this._math.addVector(this._cameraInfo.eye, option.aroundPos);
		this.updateCamera();
	},
	/**
	 * 
	 * @param {Object} option {aroundAxis, aroundPos, step}
	 */
	moveTargetAround : function(option){
		var q = o3djs.quaternions;
		var m = this._matrix4;
		
		var quaternion = q.axisRotation(option.aroundAxis, option.step);
		var matrix4 = q.quaternionToRotation(quaternion);
		//sub the aroundPos and add the aroundPos is to make sure the target is rotate around the aroundPos
		this._cameraInfo.target = m.transformPoint(matrix4, this._math.subVector(this._cameraInfo.target, option.aroundPos));
		this._cameraInfo.target = this._math.addVector(this._cameraInfo.target, option.aroundPos);
		this.updateCamera();
	}
	
 }
